﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Routing;

namespace MVCExtension.Points.Extensions.Helpers
{
    public static class ActionSelectorExtentions
    {
        public static MvcRequestModel GetRouteValues<TController>(this Expression<Func<TController, ActionResult>> actionSelector)
        {
            MvcRequestModel result = new MvcRequestModel();

            Type controllerType = typeof(TController);

            var call = GetMethodCallExpression(actionSelector, controllerType);

            result.Controller = GetControllerName(controllerType);
            result.Action = call.Method.Name;
            result.RouteValuesDictionary = GetRouteValues(call);

            return result;
        }

        private static string GetControllerName(Type controllerType)
        {
            var controllerName = controllerType.Name.EndsWith("Controller")
                ? controllerType.Name.Substring(0, controllerType.Name.Length - "Controller".Length)
                : controllerType.Name;
            return controllerName;
        }

        private static MethodCallExpression GetMethodCallExpression<TController>(Expression<Func<TController, ActionResult>> actionSelector, Type controllerType)
        {
            var call = actionSelector.Body as MethodCallExpression;
            if (call == null)
            {
                throw new ArgumentException("You must call a method of " + controllerType.Name, "actionSelector");
            }

            if (call.Object.Type != controllerType)
            {
                throw new ArgumentException("You must call a method of " + controllerType.Name, "actionSelector");
            }
            return call;
        }

        private static RouteValueDictionary GetRouteValues(MethodCallExpression call)
        {
            var routeValues = new RouteValueDictionary();

            var args = call.Arguments;
            ParameterInfo[] parameters = call.Method.GetParameters();
            var pairs = args.Select((a, i) => new
            {
                Argument = a,
                ParamName = parameters[i].Name
            });
            foreach (var argumentParameterPair in pairs)
            {
                string name = argumentParameterPair.ParamName;
                object value = argumentParameterPair.Argument.GetValue();
                if (value != null)
                {
                    var valueType = value.GetType();
                    if (valueType.IsValueType)
                    {
                        routeValues.Add(name, value);
                    }
                    else
                    {
                        var properties = PropertyInfoHelper.GetProperties(valueType);
                        foreach (var propertyInfo in properties)
                        {
                            routeValues.Add(propertyInfo.Name, propertyInfo.GetValue(value));
                        }
                    }
                }
            }
            return routeValues;
        }



        public static object GetValue(this Expression expression)
        {
            return ExpressionEvaluation.GetExpressionValue(expression, null, null);
        }


    }
}